<!--
 * @Author: 王坤
 * @Description: 金额输入框
-->
<template>
  <div class="lc-input-amount" ref="inputAmountRef">
    <div class="lc-input-amount__item">
      <Teleport to="body">
        <div
          v-if="magnify && showMagnify && amountMagnify"
          class="lc-input-amount__magnify"
          :style="magnifyPosition"
        >
          {{ amountMagnify }}
        </div>
      </Teleport>
      <div v-if="currency" class="lc-input-amount__item-prev">
        <el-select
          v-model="customCurrency"
          :disabled="disabled"
          style="width: 90px"
          @change="handleCurrencyChange"
        >
          <el-option
            v-for="(item, index) in currencyList"
            :key="index"
            :label="item[currencyLabel]"
            :value="item[currencyValue]"
          />
        </el-select>
      </div>
      <div class="lc-input-amount__item-after">
        <el-input
          v-model.trim="amountNum"
          class="lc-input"
          v-bind="$attrs"
          type="text"
          :disabled="disabled"
          @blur="handleBlur"
          @focus="handleFocus"
          @input="inputMoneyFun($event)"
          @clear="$emit('clear')"
        />
      </div>
    </div>
    <!-- 金额大写 -->
    <Teleport to="body">
      <div v-if="showMagnify" class="lc-input-amount__uppercase" :style="uppercasePosition">
        {{ currencyName }}{{ amountWord }}
      </div>
    </Teleport>
  </div>
</template>

<script setup>
import { computed, ref, unref, watch } from 'vue';
import { ElMessage } from 'element-plus';
import { amountInWords } from '@/utils/index';

const props = defineProps({
  modelValue: {
    type: [Number, String],
    default: ''
  },
  disabled: {
    type: Boolean,
    default: false
  },
  // 是否显示金额种类
  currency: {
    type: Boolean,
    default: true
  },
  currencyLabel: {
    type: String,
    default: 'label'
  },
  currencyValue: {
    type: String,
    default: 'value'
  },
  defaultValue: {
    type: [Number, String],
    default: ''
  },
  //最大小数点位数
  maxDecimalDigits: {
    type: [Number, String],
    default: '2'
  },
  //最小整数位数
  minIntegerDigits: {
    type: [Number, String],
    default: ''
  },
  //最大整数位数
  maxIntegerDigits: {
    type: [Number, String],
    default: ''
  },
  // 是否显示金额放大框
  magnify: {
    type: Boolean,
    default: true
  },
  // 币种文字，可能会从别的组件传递过来
  currencyText: {
    type: String,
    default: ''
  },
  // 金额分隔符
  separator: {
    type: String,
    default: ''
  },
  currencyList: {
    type: Array,
    default: () => [
      {
        label: '人民币',
        value: '￥'
      },
      {
        label: '美元',
        value: '$'
      },
      {
        label: '欧元',
        value: '€'
      },
      {
        label: '英镑',
        value: '£'
      },
      {
        label: '港币',
        value: 'HK$'
      },
      {
        label: '日元',
        value: 'JPY'
      },
      {
        label: '澳元',
        value: 'A$'
      }
    ]
  }
});

const inputAmountRef = ref(null);
const innerValue = ref(props.modelValue);
const emits = defineEmits(['update:modelValue']);
const customCurrency = ref('￥');
const showMagnify = ref(false);
const magnifyPosition = ref({ left: 0, top: 0 });
const uppercasePosition = ref({ left: 0, top: 0 });
/* ========== computed start ==========  */
// 大写中文金额
const amountWord = computed(() => {
  const amountArr = clearComma(amountNum.value).split('.');
  const result =
    amountArr.length > 1 ? `${amountArr[0]}.${amountArr[1].substr(0, 4)}` : amountArr[0];
  return amountInWords(result);
});
// 放大金额
const amountMagnify = computed(() => {
  return setThousandsAmount(clearComma(amountNum.value));
});
// 币种中文
const currencyName = computed(() => {
  if (props.currencyText) {
    return props.currencyText;
  }
  const currentCurrency = props.currencyList.find((item) => item.value === customCurrency.value);
  if (props.currency && currentCurrency) {
    return currentCurrency.label + props.separator;
  }
  return '';
});
const amountNum = computed(() => splitModelValue(innerValue.value));
/* ========== computed end ==========  */
/* ========== methods start ==========  */
function handleBlur(e) {
  showMagnify.value = false;
  e.target.value = setThousandsAmount(e.target.value);
}
function handleFocus(e) {
  const rect = unref(inputAmountRef).getBoundingClientRect();
  magnifyPosition.value = {
    left: rect.left + 'px',
    top: rect.top + 'px',
    minWidth: rect.width + 'px'
  };
  uppercasePosition.value = {
    left: rect.left + 'px',
    top: rect.top + rect.height + 'px',
    minWidth: rect.width + 'px'
  };
  showMagnify.value = true;
  innerValue.value && (e.target.value = splitModelValue(innerValue.value));
}
function handleCurrencyChange() {
  innerValue.value = joinModelValue(amountNum.value);
}
// 拼接币种和金额字符串，使用空格拼接
function joinModelValue(amount) {
  return props.currency ? customCurrency.value + props.separator + amount : amount;
}
function splitModelValue(amount) {
  if (amount === '') return;
  amount += '';
  const amountList = getSplitList(amount);
  if (props.currency) {
    customCurrency.value = amountList[0];
  }
  return props.currency ? (amountList.length > 1 ? amountList[1] : amountList[0]) : amount;
}
// 对输入金额进行判断格式是否正确，如果正确进行格式化处理，清除前面的零
function inputMoneyFun(_value) {
  const preValue = innerValue.value;
  let amount = clearComma(_value);
  if (!validateAmount(amount)) {
    innerValue.value = preValue;
  } else {
    innerValue.value = joinModelValue(formattedAmountFun(amount));
  }
}
// 格式化金额，并清除前面的零
function formattedAmountFun(val) {
  if (isNaN(parseFloat(val))) {
    return '';
  }
  const NumArr = val.split('.');
  const maxIntegerDigits = isNaN(parseInt(props.maxIntegerDigits))
    ? 15
    : parseInt(props.maxIntegerDigits);
  const minIntegerDigits = isNaN(parseInt(props.minIntegerDigits))
    ? 0
    : parseInt(props.minIntegerDigits);
  const maxDecimalDigits = isNaN(parseInt(props.maxDecimalDigits))
    ? 2
    : parseInt(props.maxDecimalDigits);
  let integer = parseFloat(
      Array.isArray(NumArr) && NumArr.length > 0 ? NumArr[0].slice(0, maxIntegerDigits) : ''
    ),
    decimal =
      Array.isArray(NumArr) && NumArr.length === 2 ? NumArr[1].slice(0, maxDecimalDigits) : '';
  if (`${integer}`.length < minIntegerDigits) {
    ElMessage.warning({ message: `请输入最小${minIntegerDigits}位整数位数` });
  }
  if (decimal.length > 0 || (Array.isArray(NumArr) && NumArr.length >= 2)) {
    //设置了小数点位数
    return `${integer}.${decimal}`;
  } else {
    //设置整数位数
    return integer;
  }
}
// 转化为千分位
function setThousandsAmount(number) {
  // 转为字符串，并按照.拆分
  const arr = (number + '').split('.');
  // 整数部分再拆分
  const int = arr[0].split('');
  // 小数部分
  let fraction = arr[1] || '';
  // 返回的变量
  let amount = '';
  int.reverse().forEach(function (v, i) {
    // 非第一位并且是位值是3的倍数，添加“,”
    if (i !== 0 && i % 3 === 0) {
      amount = v + ',' + amount;
    } else {
      // 正常添加字符(这是好写法)
      amount = v + amount;
    }
  });
  if (arr.length > 2) {
    for (let i = 2; i < arr.length; i++) {
      fraction += '.' + arr[i] ?? '';
    }
  }
  // 整数部分和小数部分拼接
  return amount + (arr.length > 1 ? '.' + fraction : '');
}
function validateAmount(_value) {
  const reg = /(^-?[0-9]*\.?$)|(^-?[0-9]+\.?[0-9]+$)/;
  const numArr = _value.split('.');
  if (!reg.test(_value)) {
    ElMessage.warning({ message: '不正确的输入项' });
    return false;
  }
  if (
    numArr[1] &&
    isValidInteger(props.maxDecimalDigits) &&
    numArr[1].length > props.maxDecimalDigits
  ) {
    ElMessage.warning({ message: `格式有误，小数点后只能保留${props.maxDecimalDigits}位` });
    return false;
  }
  return true;
}
// 清楚金额中的逗号
function clearComma(amount) {
  if (amount) {
    return String(amount).replace(/,/g, '');
  }
  return '';
}
function getSplitList(amount) {
  amount += '';
  if (props.separator) {
    return amount.split(props.separator);
  }
  if (props.currency) {
    const currencyObj = props.currencyList.find((item) => amount.startsWith(item.value));
    return currencyObj ? [currencyObj.value, amount.split(currencyObj.value)[1]] : [amount];
  }
  return [amount];
}
function isValidInteger(val) {
  return /^[0-9]+$/.test(val);
}
/* ========== methods end ==========  */
watch(innerValue, (_value, oldValue) => {
  let amount = 0;
  if (props.currency) {
    const amountList = getSplitList(_value);
    amount = parseFloat(amountList[1]);
    if (isNaN(amount)) {
      innerValue.value = amountList[0] + props.separator + 0;
    }
    emits('update:modelValue', amountList[0] + props.separator + amount);
  } else {
    amount = parseFloat(_value);
    if (isNaN(amount)) {
      innerValue.value = 0;
    }
    emits('update:modelValue', amount);
  }
});
</script>

<style lang="scss">
  @use '@/assets/styles/element-ui.scss' as eu;
.lc-input-amount {
  @if eu.$width == 'fullWidth' {
    width: 100%;
  } @else if eu.$width == 'mini' {
    width: 60px;
  } @else {
    width: #{eu.$width};
  }

  position: relative;
  &__magnify {
    position: fixed;
    box-sizing: border-box;
    transform: translateY(-100%);
    padding: 10px;
    margin-bottom: 5px;
    border-radius: 4px;
    word-break: break-all;
    background: #f6f6f6;
    color: #ff984c;
    font-size: 24px;
    line-height: 1;
    z-index: 9999;
  }
  &__uppercase {
    position: fixed;
    box-sizing: border-box;
    color: #ff984c;
    font-size: 16px;
    line-height: 1;
    padding-top: 4px;
    white-space: nowrap;
  }
  .lc-input-amount__item {
    width: 100%;
    display: flex;
    justify-content: space-between;

    .lc-input-amount__item-prev {
      flex-grow: 0;
      margin-right: 10px;
    }

    .lc-input-amount__item-after {
      flex: 1;
      flex-grow: 1;
      position: relative;

      .el-input {
        width: 100%;
      }
    }
  }
}

.el-form-item.is-error {
  .lc-input-amount {
    &__uppercase {
      display: none;
    }
  }
}
</style>
